// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.network;

import com.sun.management.OperatingSystemMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.nio.ByteBuffer;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.management.NotificationEmitter;
import se.krka.kahlua.vm.KahluaTable;
import zombie.Lua.LuaManager;
import zombie.characters.IsoPlayer;
import zombie.commands.PlayerType;
import zombie.core.Core;
import zombie.core.raknet.RakVoice;
import zombie.core.raknet.UdpConnection;
import zombie.core.raknet.VoiceManagerData;
import zombie.core.utils.UpdateLimit;
import zombie.core.znet.ZNetStatistics;
import zombie.debug.DebugLog;
import zombie.debug.DebugType;
import zombie.debug.LogSeverity;
import zombie.iso.IsoWorld;
import zombie.iso.WorldStreamer;
import zombie.popman.NetworkZombieManager;
import zombie.popman.NetworkZombieSimulator;
import zombie.util.StringUtils;

public class MPStatistics {
    private static final float MEM_USAGE_THRESHOLD = 0.95F;
    private static final long REQUEST_TIMEOUT = 10000L;
    private static final long STATISTICS_INTERVAL = 2000L;
    private static final long PING_INTERVAL = 1000L;
    private static final long PING_PERIOD = 10000L;
    private static final long PING_LIMIT_PERIOD = 60000L;
    private static final long PING_INTERVAL_COUNT = 60L;
    private static final long PING_LIMIT_COUNT = 20L;
    private static final long PING_LOG_COUNT = 120L;
    private static final long MAX_PING_TO_SUM = 1000L;
    private static final KahluaTable statsTable = LuaManager.platform.newTable();
    private static final KahluaTable statusTable = LuaManager.platform.newTable();
    private static final UpdateLimit ulRequestTimeout = new UpdateLimit(10000L);
    private static final UpdateLimit ulStatistics = new UpdateLimit(2000L);
    private static final UpdateLimit ulPing = new UpdateLimit(1000L);
    private static boolean serverStatisticsEnabled = false;
    private static int serverPlayers = 0;
    private static int clientPlayers = 0;
    private static int clientLastPing = -1;
    private static int clientAvgPing = -1;
    private static int clientMinPing = -1;
    private static String clientVOIPSource = "";
    private static String clientVOIPFreq = "";
    private static long clientVOIPRX = 0L;
    private static long clientVOIPTX = 0L;
    private static long serverVOIPRX = 0L;
    private static long serverVOIPTX = 0L;
    private static int serverWaitingRequests = 0;
    private static int clientSentRequests = 0;
    private static int requested1 = 0;
    private static int requested2 = 0;
    private static int pending1 = 0;
    private static int pending2 = 0;
    private static long serverCPUCores = 0L;
    private static long serverCPULoad = 0L;
    private static long serverMemMax = 0L;
    private static long serverMemFree = 0L;
    private static long serverMemTotal = 0L;
    private static long serverMemUsed = 0L;
    private static long serverRX = 0L;
    private static long serverTX = 0L;
    private static long serverResent = 0L;
    private static double serverLoss = 0.0;
    private static float serverFPS = 0.0F;
    private static long serverNetworkingUpdates = 0L;
    private static long serverNetworkingFPS = 0L;
    private static String serverRevision = "";
    private static long clientCPUCores = 0L;
    private static long clientCPULoad = 0L;
    private static long clientMemMax = 0L;
    private static long clientMemFree = 0L;
    private static long clientMemTotal = 0L;
    private static long clientMemUsed = 0L;
    private static long clientRX = 0L;
    private static long clientTX = 0L;
    private static long clientResent = 0L;
    private static double clientLoss = 0.0;
    private static float clientFPS = 0.0F;
    private static int serverStoredChunks = 0;
    private static int serverRelevantChunks = 0;
    private static int serverZombiesTotal = 0;
    private static int serverZombiesLoaded = 0;
    private static int serverZombiesSimulated = 0;
    private static int serverZombiesCulled = 0;
    private static int serverZombiesAuthorized = 0;
    private static int serverZombiesUnauthorized = 0;
    private static int serverZombiesReusable = 0;
    private static int serverZombiesUpdated = 0;
    private static int clientStoredChunks = 0;
    private static int clientRelevantChunks = 0;
    private static int clientZombiesTotal = 0;
    private static int clientZombiesLoaded = 0;
    private static int clientZombiesSimulated = 0;
    private static int clientZombiesCulled = 0;
    private static int clientZombiesAuthorized = 0;
    private static int clientZombiesUnauthorized = 0;
    private static int clientZombiesReusable = 0;
    private static int clientZombiesUpdated = 0;
    private static long zombieUpdates = 0L;
    private static long serverMinPing = 0L;
    private static long serverMaxPing = 0L;
    private static long serverAvgPing = 0L;
    private static long serverLastPing = 0L;
    private static long serverLossPing = 0L;
    private static long serverHandledPingPeriodStart = 0L;
    private static int serverHandledPingPacketIndex = 0;
    private static final ArrayList<Long> serverHandledPingHistory = new ArrayList();
    private static final HashSet<Long> serverHandledLossPingHistory = new HashSet();
    static long pingIntervalCount = 60L;
    static long pingLimitCount = 20L;
    static long maxPingToSum = 1000L;

    private static boolean isClientStatisticsEnabled() {
        boolean _boolean = false;

        for (IsoPlayer isoPlayer : IsoPlayer.players) {
            if (isoPlayer != null && isoPlayer.isShowMPInfos()) {
                _boolean = true;
                break;
            }
        }

        return _boolean;
    }

    private static void getClientZombieStatistics() {
        int _int = (int)Math.max(zombie.MovingObjectUpdateScheduler.instance.getFrameCounter() - zombieUpdates, 1L);
        clientZombiesTotal = GameClient.IDToZombieMap.values().length;
        clientZombiesLoaded = IsoWorld.instance.getCell().getZombieList().size();
        clientZombiesSimulated = clientZombiesUpdated / _int;
        clientZombiesAuthorized = NetworkZombieSimulator.getInstance().getAuthorizedZombieCount();
        clientZombiesUnauthorized = NetworkZombieSimulator.getInstance().getUnauthorizedZombieCount();
        clientZombiesReusable = zombie.VirtualZombieManager.instance.reusableZombiesSize();
        clientZombiesCulled = 0;
        clientZombiesUpdated = 0;
        zombieUpdates = zombie.MovingObjectUpdateScheduler.instance.getFrameCounter();
        serverZombiesCulled = 0;
    }

    private static void getServerZombieStatistics() {
        int _int = (int)Math.max(zombie.MovingObjectUpdateScheduler.instance.getFrameCounter() - zombieUpdates, 1L);
        serverZombiesTotal = ServerMap.instance.ZombieMap.size();
        serverZombiesLoaded = IsoWorld.instance.getCell().getZombieList().size();
        serverZombiesSimulated = serverZombiesUpdated / _int;
        serverZombiesAuthorized = 0;
        serverZombiesUnauthorized = NetworkZombieManager.getInstance().getUnauthorizedZombieCount();
        serverZombiesReusable = zombie.VirtualZombieManager.instance.reusableZombiesSize();
        serverZombiesCulled = 0;
        serverZombiesUpdated = 0;
        zombieUpdates = zombie.MovingObjectUpdateScheduler.instance.getFrameCounter();
    }

    private static void getClientChunkStatistics() {
        try {
            WorldStreamer.instance.getStatistics();
        } catch (Exception exception) {
            DebugLog.Multiplayer.printException(exception, "Error getting chunk statistics", LogSeverity.Error);
        }
    }

    public static void countChunkRequests(int sent, int _requested1, int _requested2, int _pending1, int _pending2) {
        clientSentRequests = sent;
        requested1 = _requested1;
        requested2 = _requested2;
        pending1 = _pending1;
        pending2 = _pending2;
    }

    private static void resetStatistic() {
        if (GameClient.bClient) {
            GameClient.connection.netStatistics = null;
        } else if (GameServer.bServer) {
            for (UdpConnection udpConnection : GameServer.udpEngine.connections) {
                udpConnection.netStatistics = null;
            }
        }

        serverPlayers = 0;
        clientPlayers = 0;
        clientVOIPSource = "";
        clientVOIPFreq = "";
        clientVOIPRX = 0L;
        clientVOIPTX = 0L;
        serverVOIPRX = 0L;
        serverVOIPTX = 0L;
        serverCPUCores = 0L;
        serverCPULoad = 0L;
        serverRX = 0L;
        serverTX = 0L;
        serverResent = 0L;
        serverLoss = 0.0;
        serverFPS = 0.0F;
        serverNetworkingFPS = 0L;
        serverMemMax = 0L;
        serverMemFree = 0L;
        serverMemTotal = 0L;
        serverMemUsed = 0L;
        clientCPUCores = 0L;
        clientCPULoad = 0L;
        clientRX = 0L;
        clientTX = 0L;
        clientResent = 0L;
        clientLoss = 0.0;
        clientFPS = 0.0F;
        clientMemMax = 0L;
        clientMemFree = 0L;
        clientMemTotal = 0L;
        clientMemUsed = 0L;
        serverZombiesTotal = 0;
        serverZombiesLoaded = 0;
        serverZombiesSimulated = 0;
        serverZombiesCulled = 0;
        serverZombiesAuthorized = 0;
        serverZombiesUnauthorized = 0;
        serverZombiesReusable = 0;
        serverZombiesUpdated = 0;
        clientZombiesTotal = 0;
        clientZombiesLoaded = 0;
        clientZombiesSimulated = 0;
        clientZombiesCulled = 0;
        clientZombiesAuthorized = 0;
        clientZombiesUnauthorized = 0;
        clientZombiesReusable = 0;
        clientZombiesUpdated = 0;
        serverWaitingRequests = 0;
        clientSentRequests = 0;
        requested1 = 0;
        requested2 = 0;
        pending1 = 0;
        pending2 = 0;
    }

    private static void getClientStatistics() {
        try {
            clientVOIPRX = 0L;
            clientVOIPTX = 0L;
            clientRX = 0L;
            clientTX = 0L;
            clientResent = 0L;
            clientLoss = 0.0;
            ZNetStatistics zNetStatistics = GameClient.connection.getStatistics();
            if (zNetStatistics != null) {
                clientRX = zNetStatistics.lastActualBytesReceived / 1000L;
                clientTX = zNetStatistics.lastActualBytesSent / 1000L;
                clientResent = zNetStatistics.lastUserMessageBytesResent / 1000L;
                clientLoss = zNetStatistics.packetlossLastSecond / 1000.0;
            }

            long[] _long = new long[]{-1L, -1L};
            if (RakVoice.GetChannelStatistics(GameClient.connection.getConnectedGUID(), _long)) {
                clientVOIPRX = _long[0] / 2000L;
                clientVOIPTX = _long[1] / 2000L;
            }

            clientFPS = 60.0F / zombie.GameTime.instance.FPSMultiplier;
            clientCPUCores = (long)ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
            clientCPULoad = (long)(((OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean()).getProcessCpuLoad() * 100.0);
            clientMemMax = Runtime.getRuntime().maxMemory() / 1000L / 1000L;
            clientMemFree = Runtime.getRuntime().freeMemory() / 1000L / 1000L;
            clientMemTotal = Runtime.getRuntime().totalMemory() / 1000L / 1000L;
            clientMemUsed = clientMemTotal - clientMemFree;
            clientPlayers = 0;

            for (IsoPlayer isoPlayer : IsoPlayer.players) {
                if (isoPlayer != null) {
                    clientPlayers++;
                }
            }
        } catch (Exception exception) {
        }
    }

    private static void getServerStatistics() {
        try {
            serverVOIPRX = 0L;
            serverVOIPTX = 0L;
            serverRX = 0L;
            serverTX = 0L;
            serverResent = 0L;
            serverLoss = 0.0;
            long[] _long = new long[]{-1L, -1L};

            for (UdpConnection udpConnection : GameServer.udpEngine.connections) {
                ZNetStatistics zNetStatistics = udpConnection.getStatistics();
                if (zNetStatistics != null) {
                    serverRX = serverRX + udpConnection.netStatistics.lastActualBytesReceived;
                    serverTX = serverTX + udpConnection.netStatistics.lastActualBytesSent;
                    serverResent = serverResent + udpConnection.netStatistics.lastUserMessageBytesResent;
                    serverLoss = serverLoss + udpConnection.netStatistics.packetlossLastSecond;
                }

                if (RakVoice.GetChannelStatistics(udpConnection.getConnectedGUID(), _long)) {
                    serverVOIPRX = serverVOIPRX + _long[0];
                    serverVOIPTX = serverVOIPTX + _long[1];
                }
            }

            serverRX /= 1000L;
            serverTX /= 1000L;
            serverResent /= 1000L;
            serverLoss /= 1000.0;
            serverVOIPRX /= 2000L;
            serverVOIPTX /= 2000L;
            serverFPS = 60.0F / zombie.GameTime.instance.FPSMultiplier;
            serverCPUCores = (long)ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
            serverCPULoad = (long)(((OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean()).getProcessCpuLoad() * 100.0);
            serverNetworkingFPS = 1000L * serverNetworkingUpdates / 2000L;
            serverNetworkingUpdates = 0L;
            serverMemMax = Runtime.getRuntime().maxMemory() / 1000L / 1000L;
            serverMemFree = Runtime.getRuntime().freeMemory() / 1000L / 1000L;
            serverMemTotal = Runtime.getRuntime().totalMemory() / 1000L / 1000L;
            serverMemUsed = serverMemTotal - serverMemFree;
            serverPlayers = GameServer.IDToPlayerMap.size();
        } catch (Exception exception) {
        }
    }

    private static void resetPingCounters() {
        clientLastPing = -1;
        clientAvgPing = -1;
        clientMinPing = -1;
    }

    private static void getPing(UdpConnection udpConnection) {
        try {
            if (udpConnection != null) {
                clientLastPing = udpConnection.getLastPing();
                clientAvgPing = udpConnection.getAveragePing();
                clientMinPing = udpConnection.getLowestPing();
            }
        } catch (Exception exception) {
        }
    }

    static long checkLatest(UdpConnection udpConnection, long long1) {
        if ((long)udpConnection.pingHistory.size() >= pingIntervalCount) {
            long long0 = udpConnection.pingHistory.stream().limit(pingIntervalCount).filter(_longx -> _longx > long1).count();
            if (long0 >= pingLimitCount) {
                return (long)Math.ceil(
                    (double)(
                        (float)udpConnection.pingHistory.stream().limit(pingIntervalCount).mapToLong(_long -> Math.min(maxPingToSum, _long)).sum()
                            / (float)pingIntervalCount
                    )
                );
            }
        }

        return 0L;
    }

    private static void limitPing() {
        int _int = ServerOptions.instance.PingLimit.getValue();

        for (UdpConnection udpConnection : GameServer.udpEngine.connections) {
            serverAvgPing = (long)udpConnection.getAveragePing();
            serverLastPing = (long)udpConnection.getLastPing();
            udpConnection.pingHistory.addFirst(serverLastPing);
            long _long = checkLatest(udpConnection, (long)_int);
            if (doKick(udpConnection, _long)) {
                GameServer.kick(udpConnection, "UI_Policy_Kick", "UI_OnConnectFailed_Ping");
                udpConnection.forceDisconnect("kick-ping-limit");
                GameServer.addDisconnect(udpConnection);
                DebugLog.Multiplayer.warn("Kick: player=\"%s\" type=\"%s\"", udpConnection.username, "UI_OnConnectFailed_Ping");
                DebugLog.Multiplayer.debugln("Ping: limit=%d/%d average-%d=%d", _int, pingLimitCount, pingIntervalCount, _long);
                DebugLog.Multiplayer
                    .debugln("Ping: last-%d: %s", 120L, udpConnection.pingHistory.stream().map(Object::toString).collect(Collectors.joining(", ")));
            }

            if ((long)udpConnection.pingHistory.size() > 120L) {
                udpConnection.pingHistory.removeLast();
            }
        }
    }

    public static boolean doKickWhileLoading(UdpConnection connection, long ping) {
        int _int = ServerOptions.instance.PingLimit.getValue();
        return (double)_int > ServerOptions.instance.PingLimit.getMin()
            && ping > (long)_int
            && !connection.preferredInQueue
            && !PlayerType.isPrivileged(connection.accessLevel);
    }

    public static boolean doKick(UdpConnection connection, long ping) {
        return doKickWhileLoading(connection, ping) && connection.isFullyConnected() && connection.isConnectionGraceIntervalTimeout();
    }

    private static void resetServerHandledPingCounters() {
        serverMinPing = 0L;
        serverMaxPing = 0L;
        serverAvgPing = 0L;
        serverLastPing = 0L;
        serverLossPing = 0L;
        serverHandledPingPeriodStart = 0L;
        serverHandledPingPacketIndex = 0;
        serverHandledPingHistory.clear();
        serverHandledLossPingHistory.clear();
    }

    private static void getServerHandledPing() {
        long _long = System.currentTimeMillis();
        if ((long)serverHandledPingPacketIndex == 10L) {
            serverMinPing = serverHandledPingHistory.stream().mapToLong(_longx -> _longx).min().orElse(0L);
            serverMaxPing = serverHandledPingHistory.stream().mapToLong(_longx -> _longx).max().orElse(0L);
            serverAvgPing = (long)serverHandledPingHistory.stream().mapToLong(_longx -> _longx).average().orElse(0.0);
            serverHandledPingHistory.clear();
            serverHandledPingPacketIndex = 0;
            int _int = serverHandledLossPingHistory.size();
            serverHandledLossPingHistory.removeIf(_longxx -> _long > _longxx + 10000L);
            serverLossPing = serverLossPing + (long)(_int - serverHandledLossPingHistory.size());
            serverHandledPingPeriodStart = _long;
        }

        GameClient.sendServerPing(_long);
        if (serverHandledLossPingHistory.size() > 1000) {
            serverHandledLossPingHistory.clear();
        }

        serverHandledLossPingHistory.add(_long);
        serverHandledPingPacketIndex++;
    }

    public static void setVOIPSource(VoiceManagerData.VoiceDataSource source, int freq) {
        clientVOIPSource = VoiceManagerData.VoiceDataSource.Unknown.equals(source) ? "" : source.name();
        clientVOIPFreq = freq == 0 ? "" : String.valueOf((float)freq / 1000.0F);
    }

    public static void countServerNetworkingFPS() {
        serverNetworkingUpdates++;
    }

    public static void increaseStoredChunk() {
        if (GameClient.bClient) {
            clientStoredChunks++;
        } else if (GameServer.bServer) {
            serverStoredChunks++;
        }

        decreaseRelevantChunk();
    }

    public static void decreaseStoredChunk() {
        if (GameClient.bClient) {
            clientStoredChunks--;
        } else if (GameServer.bServer) {
            serverStoredChunks--;
        }

        increaseRelevantChunk();
    }

    public static void increaseRelevantChunk() {
        if (GameClient.bClient) {
            clientRelevantChunks++;
        } else if (GameServer.bServer) {
            serverRelevantChunks++;
        }
    }

    public static void decreaseRelevantChunk() {
        if (GameClient.bClient) {
            clientRelevantChunks--;
        } else if (GameServer.bServer) {
            serverRelevantChunks--;
        }
    }

    public static void Init() {
        if (GameServer.bServer || GameClient.bClient) {
            try {
                for (MemoryPoolMXBean memoryPoolMXBean : ManagementFactory.getMemoryPoolMXBeans()) {
                    if (MemoryType.HEAP.equals(memoryPoolMXBean.getType()) && memoryPoolMXBean.isUsageThresholdSupported()) {
                        long _long = memoryPoolMXBean.getCollectionUsageThreshold();
                        String string = System.getProperty("zomboid.thresholdm");
                        if (!StringUtils.isNullOrEmpty(string)) {
                            _long = Long.parseLong(string) * 1000000L;
                        }

                        if (_long == 0L) {
                            _long = (long)((float)Runtime.getRuntime().maxMemory() * 0.95F);
                            memoryPoolMXBean.setUsageThreshold(_long);
                        }

                        if (_long > 0L) {
                            ((NotificationEmitter)ManagementFactory.getMemoryMXBean())
                                .addNotificationListener(
                                    (var1x, var2x) -> DebugLog.Multiplayer
                                            .warn(
                                                "[%s] %s (%d) free=%s",
                                                MPStatistics.class.getSimpleName(),
                                                "java.management.memory.threshold.exceeded",
                                                memoryPoolMXBean.getUsageThresholdCount(),
                                                NumberFormat.getNumberInstance().format(Runtime.getRuntime().freeMemory())
                                            ),
                                    notification -> "java.management.memory.threshold.exceeded".equals(notification.getType()),
                                    null
                                );
                        }

                        DebugLog.log(
                            DebugType.Multiplayer,
                            String.format(
                                "[%s] mem usage notification threshold=%s", MPStatistics.class.getSimpleName(), NumberFormat.getNumberInstance().format(_long)
                            )
                        );
                        break;
                    }
                }
            } catch (Exception exception) {
                DebugLog.Multiplayer.printException(exception, String.format("[%s] init error", MPStatistics.class.getSimpleName()), LogSeverity.Error);
            }

            Reset();
        }
    }

    public static void Reset() {
        resetPingCounters();
        resetServerHandledPingCounters();
        resetStatistic();
    }

    public static void Update() {
        if (GameClient.bClient) {
            if (ulPing.Check()) {
                if (!isClientStatisticsEnabled() && !NetworkAIParams.isShowPingInfo()) {
                    resetPingCounters();
                    resetServerHandledPingCounters();
                } else {
                    getPing(GameClient.connection);
                    if (isClientStatisticsEnabled()) {
                        getServerHandledPing();
                    } else {
                        resetServerHandledPingCounters();
                    }
                }
            }

            if (isClientStatisticsEnabled()) {
                if (ulStatistics.Check()) {
                    getClientStatistics();
                    getClientZombieStatistics();
                    getClientChunkStatistics();
                }
            } else {
                resetStatistic();
            }
        } else if (GameServer.bServer) {
            if (ulPing.Check()) {
                limitPing();
            }

            if (ulRequestTimeout.Check()) {
                serverStatisticsEnabled = false;
            }

            if (serverStatisticsEnabled) {
                if (ulStatistics.Check()) {
                    getServerStatistics();
                    getServerZombieStatistics();
                }
            } else {
                resetStatistic();
            }
        }
    }

    public static void requested() {
        serverStatisticsEnabled = true;
        ulRequestTimeout.Reset(10000L);
    }

    public static void clientZombieCulled() {
        clientZombiesCulled++;
    }

    public static void serverZombieCulled() {
        serverZombiesCulled++;
    }

    public static void clientZombieUpdated() {
        clientZombiesUpdated++;
    }

    public static void serverZombieUpdated() {
        serverZombiesUpdated++;
    }

    public static void write(UdpConnection connection, ByteBuffer bb) {
        bb.putLong(serverMemMax);
        bb.putLong(serverMemFree);
        bb.putLong(serverMemTotal);
        bb.putLong(serverMemUsed);
        bb.putLong(serverCPUCores);
        bb.putLong(serverCPULoad);
        bb.putLong(serverVOIPRX);
        bb.putLong(serverVOIPTX);
        bb.putLong(serverRX);
        bb.putLong(serverTX);
        bb.putLong(serverResent);
        bb.putDouble(serverLoss);
        bb.putFloat(serverFPS);
        bb.putLong(serverNetworkingFPS);
        bb.putInt(serverStoredChunks);
        bb.putInt(serverRelevantChunks);
        bb.putInt(serverZombiesTotal);
        bb.putInt(serverZombiesLoaded);
        bb.putInt(serverZombiesSimulated);
        bb.putInt(serverZombiesCulled);
        bb.putInt(NetworkZombieManager.getInstance().getAuthorizedZombieCount(connection));
        bb.putInt(serverZombiesUnauthorized);
        bb.putInt(serverZombiesReusable);
        bb.putInt(connection.playerDownloadServer.getWaitingRequests());
        bb.putInt(serverPlayers);
        zombie.GameWindow.WriteString(bb, "");
    }

    public static void parse(ByteBuffer bb) {
        long long0 = System.currentTimeMillis();
        long long1 = bb.getLong();
        serverMemMax = bb.getLong();
        serverMemFree = bb.getLong();
        serverMemTotal = bb.getLong();
        serverMemUsed = bb.getLong();
        serverCPUCores = bb.getLong();
        serverCPULoad = bb.getLong();
        serverVOIPRX = bb.getLong();
        serverVOIPTX = bb.getLong();
        serverRX = bb.getLong();
        serverTX = bb.getLong();
        serverResent = bb.getLong();
        serverLoss = bb.getDouble();
        serverFPS = bb.getFloat();
        serverNetworkingFPS = bb.getLong();
        serverStoredChunks = bb.getInt();
        serverRelevantChunks = bb.getInt();
        serverZombiesTotal = bb.getInt();
        serverZombiesLoaded = bb.getInt();
        serverZombiesSimulated = bb.getInt();
        serverZombiesCulled = serverZombiesCulled + bb.getInt();
        serverZombiesAuthorized = bb.getInt();
        serverZombiesUnauthorized = bb.getInt();
        serverZombiesReusable = bb.getInt();
        serverWaitingRequests = bb.getInt();
        serverPlayers = bb.getInt();
        serverRevision = zombie.GameWindow.ReadString(bb);
        serverHandledLossPingHistory.remove(long1);
        if (long1 >= serverHandledPingPeriodStart) {
            serverLastPing = long0 - long1;
            serverHandledPingHistory.add(serverLastPing);
        }
    }

    public static KahluaTable getLuaStatus() {
        statusTable.wipe();
        if (GameClient.bClient) {
            statusTable.rawset("serverTime", NumberFormat.getNumberInstance().format(TimeUnit.NANOSECONDS.toSeconds(zombie.GameTime.getServerTime())));
            statusTable.rawset("svnRevision", "");
            statusTable.rawset("buildDate", "");
            statusTable.rawset("buildTime", "");
            statusTable.rawset("version", Core.getInstance().getVersion());
            statusTable.rawset("lastPing", String.valueOf(clientLastPing));
            statusTable.rawset("avgPing", String.valueOf(clientAvgPing));
            statusTable.rawset("minPing", String.valueOf(clientMinPing));
        }

        return statusTable;
    }

    public static KahluaTable getLuaStatistics() {
        statsTable.wipe();
        if (GameClient.bClient) {
            statsTable.rawset("clientTime", String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
            statsTable.rawset("serverTime", NumberFormat.getNumberInstance().format(TimeUnit.NANOSECONDS.toSeconds(zombie.GameTime.getServerTime())));
            statsTable.rawset("clientRevision", String.valueOf(""));
            statsTable.rawset("serverRevision", String.valueOf(serverRevision));
            statsTable.rawset("clientPlayers", String.valueOf(clientPlayers));
            statsTable.rawset("serverPlayers", String.valueOf(serverPlayers));
            statsTable.rawset("clientVOIPSource", String.valueOf(clientVOIPSource));
            statsTable.rawset("clientVOIPFreq", String.valueOf(clientVOIPFreq));
            statsTable.rawset("clientVOIPRX", String.valueOf(clientVOIPRX));
            statsTable.rawset("clientVOIPTX", String.valueOf(clientVOIPTX));
            statsTable.rawset("clientRX", String.valueOf(clientRX));
            statsTable.rawset("clientTX", String.valueOf(clientTX));
            statsTable.rawset("clientResent", String.valueOf(clientResent));
            statsTable.rawset("clientLoss", String.valueOf((int)clientLoss));
            statsTable.rawset("serverVOIPRX", String.valueOf(serverVOIPRX));
            statsTable.rawset("serverVOIPTX", String.valueOf(serverVOIPTX));
            statsTable.rawset("serverRX", String.valueOf(serverRX));
            statsTable.rawset("serverTX", String.valueOf(serverTX));
            statsTable.rawset("serverResent", String.valueOf(serverResent));
            statsTable.rawset("serverLoss", String.valueOf((int)serverLoss));
            statsTable.rawset("clientLastPing", String.valueOf(clientLastPing));
            statsTable.rawset("clientAvgPing", String.valueOf(clientAvgPing));
            statsTable.rawset("clientMinPing", String.valueOf(clientMinPing));
            statsTable.rawset("serverPingLast", String.valueOf(serverLastPing));
            statsTable.rawset("serverPingMin", String.valueOf(serverMinPing));
            statsTable.rawset("serverPingAvg", String.valueOf(serverAvgPing));
            statsTable.rawset("serverPingMax", String.valueOf(serverMaxPing));
            statsTable.rawset("serverPingLoss", String.valueOf(serverLossPing));
            statsTable.rawset("clientCPUCores", String.valueOf(clientCPUCores));
            statsTable.rawset("clientCPULoad", String.valueOf(clientCPULoad));
            statsTable.rawset("clientMemMax", String.valueOf(clientMemMax));
            statsTable.rawset("clientMemFree", String.valueOf(clientMemFree));
            statsTable.rawset("clientMemTotal", String.valueOf(clientMemTotal));
            statsTable.rawset("clientMemUsed", String.valueOf(clientMemUsed));
            statsTable.rawset("serverCPUCores", String.valueOf(serverCPUCores));
            statsTable.rawset("serverCPULoad", String.valueOf(serverCPULoad));
            statsTable.rawset("serverMemMax", String.valueOf(serverMemMax));
            statsTable.rawset("serverMemFree", String.valueOf(serverMemFree));
            statsTable.rawset("serverMemTotal", String.valueOf(serverMemTotal));
            statsTable.rawset("serverMemUsed", String.valueOf(serverMemUsed));
            statsTable.rawset("serverNetworkingFPS", String.valueOf((int)serverNetworkingFPS));
            statsTable.rawset("serverFPS", String.valueOf((int)serverFPS));
            statsTable.rawset("clientFPS", String.valueOf((int)clientFPS));
            statsTable.rawset("serverStoredChunks", String.valueOf(serverStoredChunks));
            statsTable.rawset("serverRelevantChunks", String.valueOf(serverRelevantChunks));
            statsTable.rawset("serverZombiesTotal", String.valueOf(serverZombiesTotal));
            statsTable.rawset("serverZombiesLoaded", String.valueOf(serverZombiesLoaded));
            statsTable.rawset("serverZombiesSimulated", String.valueOf(serverZombiesSimulated));
            statsTable.rawset("serverZombiesCulled", String.valueOf(serverZombiesCulled));
            statsTable.rawset("serverZombiesAuthorized", String.valueOf(serverZombiesAuthorized));
            statsTable.rawset("serverZombiesUnauthorized", String.valueOf(serverZombiesUnauthorized));
            statsTable.rawset("serverZombiesReusable", String.valueOf(serverZombiesReusable));
            statsTable.rawset("clientStoredChunks", String.valueOf(clientStoredChunks));
            statsTable.rawset("clientRelevantChunks", String.valueOf(clientRelevantChunks));
            statsTable.rawset("clientZombiesTotal", String.valueOf(clientZombiesTotal));
            statsTable.rawset("clientZombiesLoaded", String.valueOf(clientZombiesLoaded));
            statsTable.rawset("clientZombiesSimulated", String.valueOf(clientZombiesSimulated));
            statsTable.rawset("clientZombiesCulled", String.valueOf(clientZombiesCulled));
            statsTable.rawset("clientZombiesAuthorized", String.valueOf(clientZombiesAuthorized));
            statsTable.rawset("clientZombiesUnauthorized", String.valueOf(clientZombiesUnauthorized));
            statsTable.rawset("clientZombiesReusable", String.valueOf(clientZombiesReusable));
            statsTable.rawset("serverWaitingRequests", String.valueOf(serverWaitingRequests));
            statsTable.rawset("clientSentRequests", String.valueOf(clientSentRequests));
            statsTable.rawset("requested1", String.valueOf(requested1));
            statsTable.rawset("requested2", String.valueOf(requested2));
            statsTable.rawset("pending1", String.valueOf(pending1));
            statsTable.rawset("pending2", String.valueOf(pending2));
        }

        return statsTable;
    }
}
